/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.unl.asd.controlador;

import edu.unl.asd.modelo.TipoError;
import edu.unl.asd.tablas.ErrorColumnaModeloTabla;
import edu.unl.asd.tablas.ErrorModeloTabla;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JTextArea;
import org.jdesktop.swingx.JXTable;

/**
 *
 * @author JorgeLuis
 */
public class AnalizaSintaxis {

    private List<String> entrada;
    List<String> msmaceptacion;
    private List<String> pila;
    List<edu.unl.asd.modelo.Error> errors;
    private GeneraTablaSintactica tablaSintactica;
    JTextArea txaSalida;
    ErrorModeloTabla errmt;
    JTextArea txaTablaSintactica;
    JXTable tablaErrores;

    /**
     * 
     * <b>En el Constructor Inicio la variables con las que se van  trabajar</b>
     */
    public AnalizaSintaxis(JTextArea txaTablaSintactica) {
        this.txaTablaSintactica = txaTablaSintactica;
        tablaSintactica = new GeneraTablaSintactica(txaTablaSintactica);
        msmaceptacion = new ArrayList<String>();
        entrada = new ArrayList<String>();
        pila = new ArrayList<String>();
        errors = new ArrayList<edu.unl.asd.modelo.Error>();
        pila.add("$");

    }
    /**
     * 
     * Este Método permite ir llenando la Lista Entrada con los componentes Léxicos devueltos por El Analizador Lexico concatenado al final el simbolo $ de aceptacion.
     */
    public void ingresarEntrada(String entradaTexto, JTextArea txaSalida, JXTable tablaErrores) {
        String componenteLexico;
        this.txaSalida = txaSalida;
        this.tablaErrores = tablaErrores;
        for (int i = 0; i < entradaTexto.length(); i++) {
            componenteLexico = String.valueOf(entradaTexto.charAt(i));
            if (compruebaGramatica(componenteLexico)) {
                entrada.add(componenteLexico);
            } else {
                validaCadena = false;
                pila.add("0");
            }
        }
        entrada.add("$");
    }
    boolean validaCadena = false;
    String elementoPila = "";

   
    /**
     * 
     *Método  que permite comprobar si un componente lexico de la entrada pertenece a la gramática definida en nuestra tabla sintactica que se encuentra en el arhivo xls en la carpeta recursos
     */
    public boolean compruebaGramatica(String componenteLexico) {
        boolean existeComponente = false;

        for (int i = 0; i < tablaSintactica.getTerminales().size(); i++) {
            if (!componenteLexico.equalsIgnoreCase(tablaSintactica.getTerminales().get(i))) {
                existeComponente = false;
            } else {
                existeComponente = true;
                break;
            }
        }
        return existeComponente;
    }
    /**
     * <b>Este Método permite comprobar si la Entrada es aceptada por la gramática definida en nuestra tabla sintactica que se encuentra en el arhivo xls en la carpeta recursos</b>

     */
    public void analisisSintaxis() {
        edu.unl.asd.modelo.Error error = new edu.unl.asd.modelo.Error();
        try {
            boolean recorreAnalisis = false;
            boolean esPrimeraVez = false;
            do {
  
                if (esPrimeraVez == false) {
                    agregarElementoPila("", "");
                    esPrimeraVez = true;
                }
                if (!pila.get(pila.size() - 1).equalsIgnoreCase("0")) {

                    if (pila.get(pila.size() - 1).equalsIgnoreCase("?")) {
                        removerElementoPila(pila.get(pila.size() - 1));
                    }
                if (pila.get(pila.size() - 1).equalsIgnoreCase(entrada.get(0))) {
                        removerElementoEntrada(entrada.get(0));
                        removerElementoPila(pila.get(pila.size() - 1));
                        if (entrada.size() == 1 || pila.size() == 1) {
                            if (entrada.get(0).equalsIgnoreCase("$") && pila.get(pila.size() - 1).equalsIgnoreCase("$")) {
                                validaCadena = true;
                            } else {
                                error = new edu.unl.asd.modelo.Error();
                                error.setTipoError(TipoError.SINTÁCTICO);
                                error.setNombreError("Cadena incompleta");
                                errors.add(error);
                                validaCadena = false;
                            }
                            recorreAnalisis = true;
                        } else {
                            recorreAnalisis = false;
                        }
                    } else {
                        elementoPila = pila.get(pila.size() - 1);
                        removerElementoPila(pila.get(pila.size() - 1));
                        if (!entrada.get(0).equalsIgnoreCase("$")) {
                            agregarElementoPila(elementoPila, entrada.get(0));
                        } else {
                            pila.add("0");
                        }
                        recorreAnalisis = false;
                    }
                } else {
                    error = new edu.unl.asd.modelo.Error();
                    error.setTipoError(TipoError.SINTÁCTICO);
                    error.setNombreError("La cadena está mal escrita");
                    errors.add(error);
                    validaCadena = false;
                    recorreAnalisis = true;
                }
            } while (recorreAnalisis == false);
            if (validaCadena == true) {
                creaMensajesConfirmacion("CADENA ACEPTADA", txaSalida);
            } else {
                creaMensajesConfirmacion("CADENA NO ACEPTADA", txaSalida);

            }
            errmt = new ErrorModeloTabla(errors);
            if (errmt != null) {
                errmt.fireTableDataChanged();
            }
            tablaErrores.setModel(errmt);
            ErrorColumnaModeloTabla errorColumnaModeloTabla = new ErrorColumnaModeloTabla();
            errorColumnaModeloTabla.ancho_columnas(tablaErrores);
            errorColumnaModeloTabla.disenio_cabecera(tablaErrores);
        } catch (Exception e) {
            error = new edu.unl.asd.modelo.Error();
            error.setTipoError(TipoError.LÓGICO);
            errors.add(error);
        }
    }

    /**
     * 
     * <b>Método que permite devolver un elemento de la pila a partir del analisis entre la pila y entrada</b>
     */
    public void agregarElementoPila(String Elementopila, String entrada) {
        String produccion = "";
        if (Elementopila.equalsIgnoreCase("")) {
            pila.add(tablaSintactica.getNoTerminales().get(1));
        } else {

            produccion = tablaSintactica.getTablaSintactica()[posicion_fila(Elementopila)][posicion_columna(entrada)];
            for (int i = produccion.length() - 1; i >= 0; i--) {
                pila.add(String.valueOf(produccion.charAt(i)));
            }
        }
    }

    /**
     * 
     * @param conf
     * @param txaSalida
     */
    public void creaMensajesConfirmacion(String conf, JTextArea txaSalida) {
        try {
            msmaceptacion.add(conf);
            txaSalida.setText(msmaceptacion.toString().replace("[", "").replace("]", ""));
        } catch (Exception e) {
        }
    }

    /**
     * 
     * @param ruta
     * @return
     */
    public String leer_archivo(String ruta) {
        String contenido = "";
        FileReader fr = null;
        BufferedReader br = null;
        try {
            fr = new FileReader(ruta);
            br = new BufferedReader(fr);
            String linea = "";
            while ((linea = br.readLine()) != null) {
                contenido += linea + "\n";
            }
        } catch (Exception e) {
        }
        return contenido;
    }

    /**
     * 
     * @param area_texto
     * @param tokenizer
     */
    public void abrir(JTextArea area_texto, StringTokenizer tokenizer) {

        if (area_texto.getText().equals("")) {
            JFileChooser fileChooser = new JFileChooser();

            int seleccion = fileChooser.showOpenDialog(area_texto);
            if (seleccion == JFileChooser.APPROVE_OPTION) {
                String contenido = leer_archivo(fileChooser.getSelectedFile().getAbsolutePath());
                if (contenido != null) {
                    area_texto.setText("");
                    area_texto.setText(contenido);

                } else {
                    JOptionPane.showMessageDialog(null, "Archivo incorrecto", "ATENCIÓN", JOptionPane.PLAIN_MESSAGE);
                }
            }
        } else {
            int confirmar = JOptionPane.showConfirmDialog(null, "Desea Guardar el texto actual", "Guardar", JOptionPane.YES_OPTION);
            if (confirmar == JOptionPane.YES_OPTION) {
                guardar_archivo(tokenizer, area_texto);
                area_texto.setText(null);
            } else {
                JFileChooser fileChooser = new JFileChooser();

                int seleccion = fileChooser.showOpenDialog(area_texto);
                if (seleccion == JFileChooser.APPROVE_OPTION) {
                    String contenido = leer_archivo(fileChooser.getSelectedFile().getAbsolutePath());
                    if (contenido != null) {
                        area_texto.setText("");
                        area_texto.setText(contenido);

                    } else {
                        JOptionPane.showMessageDialog(null, "Archivo incorrecto", "ATENCIÓN", JOptionPane.PLAIN_MESSAGE);
                    }
                }
            }
        }

    }

    /**
     * 
     * @param tokenizer
     * @param area_texto
     */
    public void guardar_archivo(StringTokenizer tokenizer, JTextArea area_texto) {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

        JFileChooser fileChooser = new JFileChooser();

        int seleccion = fileChooser.showSaveDialog(area_texto);

        if (seleccion == JFileChooser.APPROVE_OPTION) {
            File file = new File(fileChooser.getSelectedFile().getAbsolutePath() + ".txt");
            try {
                if (file.createNewFile()) {
                    BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true), "UTF8"));
                    while (tokenizer.hasMoreElements()) {
                        out.write(tokenizer.nextToken());
                        out.write("\n");
                    }
                    out.close();
                    JOptionPane.showMessageDialog(null, "Archivo Guardado Correctamente", "CORRECTO", JOptionPane.INFORMATION_MESSAGE);
                } else {
                    JOptionPane.showMessageDialog(null, "EL ARCHIVO YA EXISTE", "ATENCIÓN", JOptionPane.WARNING_MESSAGE);
                }
            } catch (Exception e) {
                JOptionPane.showMessageDialog(null, e, "ATENCIÓN", JOptionPane.PLAIN_MESSAGE);
            }
        }
    }

    /**
     * 
     * Método que permite comprobar si el simbolo (noTerminal) se encuentra en la Lista de No Terminales
     */
    public int posicion_fila(String noterminal) {
        int posicion = 0;

        for (int i = 1; i < tablaSintactica.getNoTerminales().size(); i++) {
            if (noterminal.equalsIgnoreCase(tablaSintactica.getNoTerminales().get(i))) {
                posicion = i;
                break;
            }
        }
        return posicion;

    }
   

    /**
     * 
     * <b>Método que permite comprobar si el simbolo (Terminal) se encuentra en la Lista de  Terminales</b>
     */
    public int posicion_columna(String terminal) {
        int posicion = 0;

        for (int i = 0; i < tablaSintactica.getTerminales().size(); i++) {
            if (terminal.equalsIgnoreCase(tablaSintactica.getTerminales().get(i))) {
                posicion = i;
                break;
            }
        }
        return posicion;

    }

    /**
     * 
     * @param elemento
     */
    public void removerElementoPila(String elemento) {
        pila.remove(elemento);
    }

    /**
     * 
     * @param elemento
     */
    public void removerElementoEntrada(String elemento) {
        entrada.remove(elemento);
    }

    /**
     * 
     * @return
     */
    public List<String> getEntrada() {
        return entrada;
    }

    /**
     * 
     * @param entrada
     */
    public void setEntrada(List<String> entrada) {
        this.entrada = entrada;
    }

    /**
     * 
     * @return
     */
    public List<String> getPila() {
        return pila;
    }

    /**
     * 
     * @param pila
     */
    public void setPila(List<String> pila) {
        this.pila = pila;
    }

    /**
     * 
     * @return
     */
    public GeneraTablaSintactica getTablaSintactica() {
        return tablaSintactica;
    }

    /**
     * 
     * @param tablaSintactica
     */
    public void setTablaSintactica(GeneraTablaSintactica tablaSintactica) {
        this.tablaSintactica = tablaSintactica;
    }
}
